#include "Persistor.h"
#include "../common/utils/SurfaceUtils.h"
#include "../common/lib/SDL_rotozoom.h"
#include "../common/Constantes.h"

//Valid tags:
#define ASTEROIDS "asteroids"
#define TIPOS_ASTEROIDE "tiposAsteroide"
#define TIPO_ASTEROIDE "tipoAsteroide"
#define ASTEROIDES "asteroides"
#define XML_ASTEROIDE "asteroide"
#define NAVE "nave"


#define XML_DIRECCION 	"direccion"
#define XML_TIPO		"tipo"
#define XML_VELOCIDAD	"velocidad"
#define XML_X			"x"
#define XML_Y			"y"
#define XML_NOMBRE		"nombre"
#define XML_IMAGEN		"imagen"
#define XML_ANCHO		"ancho"
#define XML_ALTO		"alto"
#define XML_ROTACION	"rotacion"

#define XML_VELROTACION	"velRotacion"
#define XML_VELMAXIMA	"velMaxima"
#define XML_PROPULSION	"propulsion"
#define XML_FRICCION	"friccion"
#define XML_DURACIONBALA "duracionBala"


static int width_scenario;

static int height_scenario;


void Persistor::getDimensions(std::string scenarioName,int* width, int* height){
	//Path to scenario
	std::string scenariosPath = XML_PATH + scenarioName;
	//Set the doc file
	TiXmlDocument doc(scenariosPath.c_str());

	//Is a valid file?
	if (doc.LoadFile()) {
		TiXmlElement* root = doc.RootElement();
		if (strcmp(root->Value(),ASTEROIDS) == 0){

			int tmp_width = -1;
			root->Attribute(XML_ANCHO,&tmp_width);

			if(tmp_width <= 0){
				Logger::trace("Persistor","getDimensions",root,"No se encontró un ancho del escenario válido");
			}else{
				*width = tmp_width;
			}

			int tmp_height = -1;
			root->Attribute(XML_ALTO,&tmp_height);

			if(tmp_height <= 0){
				Logger::trace("Persistor","getDimensions",root,"No se encontró un alto del escenario válido");
			}else{
				*height = tmp_height;
			}
		}
	}
}


int Persistor::getElements(std::string scenarioName, std::list<Asteroide*>& retorno) {
	//Path to scenario
	std::string scenariosPath = XML_PATH + scenarioName;
	//Set the doc file
	TiXmlDocument doc(scenariosPath.c_str());

	//Is a valid file?
	if (!doc.LoadFile()) {
		return INVALID_FILE;
	}

	TiXmlElement* root = doc.RootElement();

	//root element is a ASTEROIDS ?
	if (strcmp(root->Value(),ASTEROIDS) != 0){
		return INVALID_ROOT_VALUE;
	}

	int tmp_width = -1;
	root->Attribute(XML_ANCHO,&tmp_width);

	if(tmp_width > 0){
		width_scenario = tmp_width;
	}

	int tmp_height = -1;
	root->Attribute(XML_ALTO,&tmp_height);

	if(tmp_height > 0){
		height_scenario = tmp_height;
	}

	///Get the list of TiposAsteroide
	std::list<DTOTipoAsteroide*> tiposAsteroide;
	int tiposReturn;
	tiposReturn = getTiposAsteroide(root,tiposAsteroide);
	if ( tiposReturn != PERSISTER_SUCCESS)
		return tiposReturn;

	//Get the asteroides
	std::list<DTOAsteroide*> asteroides;
	tiposReturn = getAsteroides(root,asteroides,tiposAsteroide);
	if ( tiposReturn != PERSISTER_SUCCESS)
		return tiposReturn;

	//Crear los Asteroides
	DTOAsteroide* asteroide;
	while(!asteroides.empty()){
		asteroide = asteroides.back();
		asteroides.pop_back();

		retorno.push_back(new Asteroide(asteroide));
		delete asteroide;
	}


	return PERSISTER_SUCCESS;
}

std::list< std::pair<std::string,std::string>* >* Persistor::getShipsModels(){
	std::list< std::pair<std::string,std::string>* >* availableShips = new std::list< std::pair<std::string,std::string>* >();


	//Path to scenario
	std::string scenariosPath = XML_SHIPS_PATH;
	//Set the doc file
	TiXmlDocument doc(scenariosPath.c_str());

	//Is a valid file?
	if (!doc.LoadFile()) {
		return NULL;
	}

	TiXmlElement* root = doc.RootElement();


	//Get the Ship
	std::list< std::pair<std::string,DTOShipStatus*>* > shipsStatus;
	int tiposReturn = getShips(root,&shipsStatus);
	if ( tiposReturn != PERSISTER_SUCCESS)
		return NULL;


	std::pair<std::string,DTOShipStatus*>* shipAnalized = NULL;
	while(!shipsStatus.empty()){

		shipAnalized = shipsStatus.back();

		availableShips->push_back(new std::pair<std::string,std::string>(shipAnalized->first,shipAnalized->second->imagen));

		delete shipAnalized->second;
		delete shipAnalized;

		shipsStatus.pop_back();
	}



	return availableShips;
}


ShipStatus* Persistor::getShipByName(std::string shipName){
	ShipStatus *shipRetorno = NULL;


	//Path to scenario
	std::string scenariosPath = XML_SHIPS_PATH;
	//Set the doc file
	TiXmlDocument doc(scenariosPath.c_str());

	//Is a valid file?
	if (!doc.LoadFile()) {
		return NULL;
	}

	TiXmlElement* root = doc.RootElement();


	//Get the Ship
	std::list< std::pair<std::string,DTOShipStatus*>* > shipsStatus;
	int tiposReturn = getShips(root,&shipsStatus);
	if ( tiposReturn != PERSISTER_SUCCESS)
		return NULL;


	//Crear la Nave
	std::pair<std::string,DTOShipStatus*>* shipAnalized;
	while(!shipsStatus.empty()){
		shipAnalized = shipsStatus.back();
		shipsStatus.pop_back();

		//TODO No valido que pueda haber 2 con mismo nombre
		if(shipAnalized->first.compare(shipName) == 0)
			shipRetorno = new ShipStatus(shipAnalized->second);

		delete shipAnalized->second;
		delete shipAnalized;
	}

	return shipRetorno;
}


int Persistor::getAsteroides(TiXmlElement* root,std::list<DTOAsteroide*>& asteroides,std::list<DTOTipoAsteroide*>& tiposAsteroide){

	DTOTipoAsteroide* tipo;
	int x;
	int y;
	int direccion;
	std::string tipoString;
	int velocidad;
	bool encontrado = false;
	int nextId = 0;


	//Recorro el xml buscando el primer "asteroides"
	TiXmlElement* tmpXmlElement;
	TiXmlElement* xmlElement;
	for (tmpXmlElement = root->FirstChildElement(); tmpXmlElement;tmpXmlElement = tmpXmlElement->NextSiblingElement()) {
		//element is a tipoAsteroide ?
		if (strcmp(tmpXmlElement->Value(),ASTEROIDES) == 0){
			if(!encontrado){
				Logger::trace("Persistor", "getAsteroides",tmpXmlElement,
					"Se encontro un elemento asteroides");
				encontrado = true;
				xmlElement = tmpXmlElement;
			}else{
				Logger::trace("Persistor", "getAsteroides",tmpXmlElement,"Se encontro otro elemento asteroides");
			}
		}
	}

	// Si no se encontro una nave
	if (!encontrado){
		Logger::trace("Persistor", "getAsteroides",root, "No se encontraron asteroids");
	}else{
		TiXmlElement* asteroide;
		for (asteroide = xmlElement->FirstChildElement(); asteroide;asteroide = asteroide->NextSiblingElement()) {
			tipoString = AS_DEFAULT_TIPO;
			const char* tipoS = asteroide->Attribute(XML_TIPO);
			if (tipoS != NULL){
				tipoString = tipoS;
			}else{
				Logger::trace("Persistor","getAsteroides",asteroide,"No se encontró el tipo del asteroide");
			}


			x = AS_DEFAULT_X;

			int tmp_x = -1;
			asteroide->Attribute(XML_X,&tmp_x);

			if(tmp_x < 0){
				Logger::trace("Persistor","getAsteroides",asteroide,"No se encontró un x del asteroide válido");
			}else{
				x = tmp_x;
			}


			y = AS_DEFAULT_Y;

			int tmp_y = -1;
			asteroide->Attribute(XML_Y,&tmp_y);

			if(tmp_y < 0){
				Logger::trace("Persistor","getAsteroides",asteroide,"No se encontró un y del asteroide válido");
			}else{
				y = tmp_y;
			}


			direccion = AS_DEFAULT_DIREC;

			int tmp_dir = -1;
			asteroide->Attribute(XML_DIRECCION,&tmp_dir);

			if(tmp_dir < 0 || tmp_dir > 359){
				Logger::trace("Persistor","getAsteroides",asteroide,"No se encontró una dirección del asteroide válida");
			}else{
				direccion = tmp_dir;
			}


			velocidad = AS_DEFAULT_VEL;

			int tmp_velocidad = -1;
			asteroide->Attribute(XML_VELOCIDAD,&tmp_velocidad);

			if(tmp_velocidad <  0){
				Logger::trace("Persistor","getAsteroides",asteroide,"No se encontró una velocidad del asteroide válida");
			}else{
				velocidad = tmp_velocidad;
			}


			//Get the type of the asteroide from the list
			tipo = findAsteroideType(tipoString,tiposAsteroide);

			asteroides.push_back(new DTOAsteroide(nextId, AS_NIVEL_INICIAL,tipo,x,y,direccion,velocidad,true));
			nextId++;
		}
	}

	return PERSISTER_SUCCESS;
}

//TODO esto es POR DEMASIA de largo y a la vez inentendible..
//TODO extraer las validacion.. no corresponde al persistidor validar XML...
std::pair<std::string,DTOShipStatus*>* Persistor::getShip(TiXmlElement *shipElementXML){

	std::string imagen;
	float velRotacion;
	int velMaxima;
	int propulsion;
	int friccion;
	int duracionBala;

    imagen = SH_DEFAULT_IMAGE;
    const char*img = shipElementXML->Attribute(XML_IMAGEN);
	imagen = img;


    velRotacion = SH_DEFAULT_VELROT;
    double tmp_velRot = -1;
    shipElementXML->Attribute(XML_VELROTACION,&tmp_velRot);
    if(tmp_velRot < 0){
        Logger::trace("Persistor", "getShip", shipElementXML, "No se encontró una velocidad de rotación de la nave válida");
    }else{
        velRotacion = tmp_velRot;
    }
    velMaxima = SH_DEFAULT_VELMAX;
    int tmp_velMax = -1;
    shipElementXML->Attribute(XML_VELMAXIMA,&tmp_velMax);
    if(tmp_velMax < 0){
        Logger::trace("Persistor", "getShip", shipElementXML, "No se encontró una velocidad máxima de la nave válida");
    }else{
        velMaxima = tmp_velMax;
    }
    propulsion = SH_DEFAULT_PROPULSION;
    int tmp_prop = -1;
    shipElementXML->Attribute(XML_PROPULSION,&tmp_prop);
    if(tmp_prop < 0){
        Logger::trace("Persistor", "getShip", shipElementXML, "No se encontró una propulsión de la nave válida");
    }else{
        propulsion = tmp_prop;
    }
    friccion = SH_DEFAULT_FRICCION;
    int tmp_friccion = -1;
    shipElementXML->Attribute(XML_FRICCION,&tmp_friccion);
    if(tmp_friccion < 0){
        Logger::trace("Persistor", "getShip", shipElementXML, "No se encontró una fricción de la nave válida");
    }else{
        friccion = tmp_friccion;
    }
    duracionBala = SH_DEFAULT_DURBALA;
    int tmp_duracion = -1;
    shipElementXML->Attribute(XML_DURACIONBALA,&tmp_duracion);
    if(tmp_duracion < 0){
        Logger::trace("Persistor", "getShip", shipElementXML, "No se encontró una duración de la bala válida");
    }else{
        duracionBala = tmp_duracion;
    }
    //Nombre
    std::string nombre;
    nombre = shipElementXML->Attribute(XML_NOMBRE);

    std::list<DTOEquipment*> userEquipment;

	return new std::pair<std::string,DTOShipStatus*>( nombre, new DTOShipStatus(SERVER_OWN,imagen, velRotacion, velMaxima, propulsion, friccion, duracionBala,SH_DEFAULT_ANGLE,SH_DEFAULT_X,SH_DEFAULT_Y,userEquipment,true,3,100));
}

int Persistor::getShips(TiXmlElement* root,std::list< std::pair<std::string,DTOShipStatus*>* >* ships){

	//Recorro el xml buscando el primer "nave"
	TiXmlElement* tmpShipElement;
	for (tmpShipElement = root; tmpShipElement;tmpShipElement = tmpShipElement->NextSiblingElement()) {
		//element is a NAVE ?
		if (strcmp(tmpShipElement->Value(),NAVE) == 0){
			Logger::trace("Persistor", "getShip",tmpShipElement,	"Se encontro un elemento nave" );

			ships->push_back(getShip(tmpShipElement));
		}
	}


	return PERSISTER_SUCCESS;
}


DTOTipoAsteroide* Persistor::findAsteroideType(std::string tipoString,std::list<DTOTipoAsteroide*>& tiposAsteroide){
	std::list<DTOTipoAsteroide*>::iterator itTipos;
	for(itTipos = tiposAsteroide.begin(); itTipos!=tiposAsteroide.end();itTipos++){
		if ((*itTipos)->nombre.compare(tipoString) == 0) {
			return *itTipos;
		}
	}

	Logger::trace("Persistor", "findAsteroideType","No se encontro el tipo buscado, devuelvo tipo default");
	return new DTOTipoAsteroide();
}

bool Persistor::existsAsteroideType(std::string tipoString,std::list<DTOTipoAsteroide*> tiposAsteroide){
	std::list<DTOTipoAsteroide*>::iterator itTipos;
	for(itTipos = tiposAsteroide.begin(); itTipos!=tiposAsteroide.end();itTipos++){
		if ((*itTipos)->nombre.compare(tipoString)==0) {
			Logger::trace("Persistor", "existsAsteroideType","Se encontro el tipo buscado, devuelvo true");
			return true;
		}
	}

	Logger::trace("Persistor", "existsAsteroideType","No se encontro el tipo buscado, devuelvo false");
	return false;
}

int Persistor::getTiposAsteroide(TiXmlElement* root,std::list<DTOTipoAsteroide*>& tiposAsteroide){

	std::string nombre;
	std::string imagen;
	int ancho;
	int alto;
	int rotacion;
	bool encontrado = false;

	TiXmlElement* tmpTiposAsteroideElement;
	TiXmlElement* tiposAsteroideElement;
	for (tmpTiposAsteroideElement = root->FirstChildElement(); tmpTiposAsteroideElement; tmpTiposAsteroideElement = tmpTiposAsteroideElement->NextSiblingElement()) {

		if (strcmp(tmpTiposAsteroideElement->Value(),TIPOS_ASTEROIDE) == 0){
			if(!encontrado){
				Logger::trace("Persistor", "getTiposAsteroide",tmpTiposAsteroideElement, "Se encontro un elemento tiposAsteroide");
				encontrado = true;
				tiposAsteroideElement = tmpTiposAsteroideElement;
			}else{
				Logger::trace("Persistor", "getTiposAsteroide",tmpTiposAsteroideElement,
									"Se encontro otro elemento tiposAsteroide, sera ignorado");
			}
		}
	}

	if (!encontrado){
		Logger::trace("Persistor", "getTiposAsteroide", tiposAsteroideElement,"No se encontraron elementos tiposAsteroide");
	}else{

		TiXmlElement* tipoAsteroide;
		for (tipoAsteroide = tiposAsteroideElement->FirstChildElement(); tipoAsteroide; tipoAsteroide = tipoAsteroide->NextSiblingElement()) {

			if (strcmp(tipoAsteroide->Value(),TIPO_ASTEROIDE) == 0){
				nombre = TA_DEFAULT_NAME;
				const char* nom = tipoAsteroide->Attribute(XML_NOMBRE);
				if (nom != NULL){
					nombre = nom;
				}else{
					Logger::trace("Persistor","getTiposAsteroide",tipoAsteroide,"No se encontró un nombre del tipo de asteroide");
				}


				imagen = TA_DEFAULT_IMAGE;
				const char*img = tipoAsteroide->Attribute(XML_IMAGEN);
				if (img != NULL){
					imagen = img;
				}else{
					Logger::trace("Persistor","getTiposAsteroide",tipoAsteroide,"No se encontró una imagen del tipo de asteroide");
				}

				//Check that the image is a valid one and exists
				if (!FileChecker::fileExists(std::string(img))) {
					Logger::trace("Persistor","getTiposAsteroide",tipoAsteroide,"No se encontró una imagen valida del tipo de asteroide");
					imagen = TA_DEFAULT_IMAGE;
				}


				ancho = TA_DEFAULT_WIDTH;
				int tmp_ancho = -1;
				tipoAsteroide->Attribute(XML_ANCHO,&tmp_ancho);

				if(tmp_ancho < 0){
					Logger::trace("Persistor","getTiposAsteroide",tipoAsteroide,"No se encontró un ancho del tipo de asteroide válido");
				}else{
					ancho = tmp_ancho;
				}


				alto = TA_DEFAULT_HEIGTH;

				int tmp_alto = -1;
				tipoAsteroide->Attribute(XML_ALTO,&tmp_alto);

				if(tmp_alto < 0){
					Logger::trace("Persistor","getTiposAsteroide",tipoAsteroide,"No se encontró un alto del tipo de asteroide válido");
				}else{
					alto = tmp_alto;
				}


				rotacion = TA_DEFAULT_ROTATION;

				int tmp_rotacion = -1;
				tipoAsteroide->Attribute(XML_ROTACION,&tmp_rotacion);

				if(tmp_rotacion < 0 || tmp_rotacion > 359){
					Logger::trace("Persistor","getTiposAsteroide",tipoAsteroide,"No se encontró una rotación del tipo de asteroide válida");
				}else{
					rotacion = tmp_rotacion;
				}


				if(!existsAsteroideType(nombre,tiposAsteroide)){
					//Add the element to the list
					Logger::trace("Persistor", "getTiposAsteroide",tipoAsteroide,"Nuevo tipo '" + nombre + "', lo agrego");
					tiposAsteroide.push_back(new DTOTipoAsteroide(nombre,imagen,ancho,alto,rotacion));
				}else{
					Logger::trace("Persistor", "getTiposAsteroide","Ya existe el tipo '" + nombre + "', lo ignoro");
				}
			}else{
				Logger::trace("Persistor", "getTiposAsteroide",tipoAsteroide,"Elemento diferente a tipoAsteroide, lo ignoro");
			}
		}
	}

	return PERSISTER_SUCCESS;
}

std::string Persistor::getScenarioFile(std::string scenarioName){

	std::string scenariosPath = XML_PATH;
	scenariosPath += SCENARIOS_FILE;

	std::string tmpScenarioFile = "";

	//Set the doc file
	TiXmlDocument doc(scenariosPath.c_str());

	//Is a valid file?
	if (doc.LoadFile()) {
		TiXmlElement* root = doc.RootElement();
		if (strcmp(root->Value(),SCENARIOS) == 0){
			TiXmlElement* scenarioElement;
			for (scenarioElement = root->FirstChildElement(); scenarioElement; scenarioElement = scenarioElement->NextSiblingElement()) {
				if (strcmp(scenarioElement->Value(),SCENARIO) == 0){
					const char* scenName = scenarioElement->Attribute(XML_NAME);
					if (scenName == scenarioName){
						const char* scenFile = scenarioElement->Attribute(XML_FILE);
						tmpScenarioFile =  scenFile;
					}
				}
			}
		}

	}

	return tmpScenarioFile;
}
